/*
*  Impp - Instant Messenger
*
*  Copyright (C) 2001-2009 Oliver Schneider
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "Impp.h"

#include <wx/dir.h>
#include <wx/file.h>

#include "events/AvatarChangedEvent.h"
#include "events/FileTransfareEvent.h"
#include "events/StatusEvent.h"
#include "events/MessageReadEvent.h"
#include "events/BuddyChangedEvent.h"
#include "events/GroupChangedEvent.h"
#include "events/SelfChangedEvent.h"
#include "events/SettingChangedEvent.h"
#include "events/ImppCommandEvent.h"
#include "events/AccountEvent.h"

#include "actions/ConnectAction.h"
#include "actions/RequestAvatarAction.h"
#include "actions/SendMessageAction.h"
#include "actions/SetStatusAction.h"
#include "actions/IncominFileTransfareAction.h"

#include "../modules/AuthConfirmDialog.h"


namespace Impp {

BEGIN_EVENT_TABLE(Impp, wxEvtHandler)
END_EVENT_TABLE()


Impp* Impp::instanz = 0;

Impp::Impp() :
	wxEvtHandler(),
	rootGroup(NULL),
	notInListGroup(NULL),
	settings(NULL),
	actions(NULL),
	modules(NULL),
	db(NULL),
	notfications(NULL),
	masterVisibility(PRICACY_BLOCK_ONLY_DENYLIST),
	masterConnectionState(CONNECTION_STATE_DISCONNECTED)

{
}

Impp::~Impp()
{
	//main cleanup is done by close
}

void Impp::init(const wxString & pProfilDir){

	if(!pProfilDir.IsEmpty()){

		wxString ppd = pProfilDir;
		ppd.Replace(wxT("\\"), wxT("/"));

		if(ppd.EndsWith(wxT("/"))){
			profilFolder = ppd.Truncate(ppd.Len()-1);
		}else{
			profilFolder = ppd;
		}
	}else{
		#ifdef __WXMSW__
			profilFolder = ::wxGetCwd();
		#else
			profilFolder = ::wxGetHomeDir();
			profilFolder << wxT("/.impp");
		#endif
	}

	if(!wxDirExists(profilFolder)){
		wxLogError(wxT("Profilfolder: % didn't exist!"), profilFolder.c_str());
		wxExit();
	}

	pixmapsFolder = ::wxGetCwd();
	pixmapsFolder << wxT("/pixmaps");
wxLogDebug(pixmapsFolder);
	#ifndef __WXMSW__
	if(!wxDirExists(pixmapsFolder)){
		pixmapsFolder = wxT("/usr/share/pixmaps/impp");
		if(!wxDirExists(pixmapsFolder)){
			wxLogError(wxT("Pixmapsfolder didn't exsist!"));
			wxExit();
		}
	}
	#endif
	
	

	wxString avatarDir = getAvatarDir();

	wxLogDebug(wxT("folder: %s"),profilFolder.c_str());

	//check for folders...
	if(!wxDir::Exists(profilFolder)){
		::wxMkdir(profilFolder);
		wxLogDebug(wxT("profil dir didn't exsist -> creteated!"));
	}
	if(!wxDir::Exists(avatarDir)){
		::wxMkdir(avatarDir);
		wxLogDebug(wxT("avatar dir didn't exsist -> creteated!"));
	}

	wxString profilFile = profilFolder;
	profilFile <<  wxT("/test.db");


	//Database
	db = new Database(profilFile);
	//Buddies and Groups
	rootGroup = db->getRootGroup();
	db->getGroupsWithBuddies(groups,buddies);
	//Accounts
	db->getAccounts(accounts);

	//it is very important that getNotInListGroup() is called after getGroupsWithBuddies()
	notInListGroup = db->getNotInListGroup(groups);

	//Settings
	actions = new ActionManager();
	settings = new Settings();
	notfications = new NotificationSystem();

	modules = new ModuleManager();

	std::vector<Account*>::iterator ia;
	for(ia = accounts.begin(); ia != accounts.end(); ia++){
		Account* account = *ia;

		account->self->setStatus(OFFLINE);

		if(account->isActive()){
			account->protocol = modules->loadAndInitProtocolInstance(account);

			if(account->protocol == NULL){
				wxLogDebug(wxT("Impp::init: Could not load Account"));
				ia = accounts.erase(ia);
				delete account;
			}
		}

	}

	modules->loadAndInitCoreInterfaceModules();

	DBModule dbImpp = db->getModule(wxT("Impp"));

	if(dbImpp.getIntValue(wxT("firstRun"),1)){
		iUserInterface* ui = modules->getUserInterfaceModule();
		if(ui != NULL){
			bool succes = ui->showFirstRunWizzard();
			if(!succes){
				close();
			}
		}
		dbImpp.setIntValue(wxT("firstRun"),0);
	}

	for(ia = accounts.begin(); ia != accounts.end(); ia++){
		Account* account = *ia;
		wxLogDebug(wxT("Account: id=%i\tclassname=%s\tactive=%i"),
				account->id.ToLong(), account->classname.c_str(), account->isActive());
	}

}

wxString Impp::getProfilDir(){
	return profilFolder;
}
wxString Impp::getAvatarDir(){
	return wxString(profilFolder) << wxT("/avatar");
}

wxString Impp::getPixmapsDir(){
	return pixmapsFolder;
}

Impp* Impp::exemplar(){
	  if( instanz == 0 )
	    instanz = new Impp();
	  return instanz;
}

void Impp::addGroupToGroupsAndBuddylist(Group* group){
	wxASSERT(group!=NULL);

	groups.push_back(group);

	GroupChangedEvent event(group, GROUP_CHANGED_ADDED, EVT_GROUP_CHANGED);
	AddPendingEvent(event);
}


void Impp::addBuddyToGroupsAndBuddylist(Buddy* b, Group* subGroup){
	wxASSERT(b!=NULL);
	wxASSERT(subGroup!=NULL);

	buddies.push_back(b);
	subGroup->addBuddy(b);

	BuddyChangedEvent event(b, BUDDY_CHANGED_ADDED, EVT_BUDDY_CHANGED);
	AddPendingEvent(event);
}

Group* Impp::getGroup(const size_t id){
	for (size_t i = 0; i < groups.size(); ++i) {
		if(groups[i]->getId() == id){
			return groups[i];
		}
	}
	return NULL;
}

Buddy* Impp::getBuddy(const db_id & accountId, const wxString & screenName){
	for (size_t i = 0; i < buddies.size(); ++i) {
		if(buddies[i]->getAccountId() == accountId && buddies[i]->getScreenName() == screenName){
			return buddies[i];
		}
	}
	return NULL;
}

void Impp::processBuddyStatusChanged(Buddy* b,const Status & s, const wxString & statusMsg, const StatusConnectionType & statusConnectionType){
	wxASSERT(b!=NULL);
	//get old status and statusMessage
	Status oldStatus = b->getStatus();
	wxString oldStatusMsg = b->getStatusMsg();
	//set the new infos
	b->setStatus(s);
	b->setStatusMsg(statusMsg);
	//signs the buddy on or off or is it a status change?
	StatusEventType statusEventType;

	if(s != OFFLINE && s != UNKNOWN && (oldStatus == OFFLINE || oldStatus == UNKNOWN)){
		//buddy signed on
		statusEventType=STATUS_EVENT_BUDDY_SIGNED_ON;
		b->setExtendedInfo(BUDDY_EXT_LAST_SEEN, wxString::Format(wxT("%i"),wxDateTime::GetTimeNow()));
	}else if (s == OFFLINE && oldStatus!=UNKNOWN){
		//buddy logged out
		statusEventType=STATUS_EVENT_BUDDY_SIGNED_OFF;
	}else if (s != OFFLINE && oldStatus!=UNKNOWN){
		//buddy changed the status
		statusEventType=STATUS_EVENT_BUDDY_STATUS_CHANGED;
	}

	//create the event
	StatusEvent event(b,oldStatus,s,oldStatusMsg,statusMsg, statusEventType, statusConnectionType, EVT_BUDDY_STATUS_CHANGED);
	//inform the buddylist and the chatwindow
	AddPendingEvent(event);
}

void Impp::processMessageSendCallback(const ActionId & id, Buddy* buddy, MessageState state, MessageError error, const wxString & errorString){
	wxASSERT(buddy!=NULL);
	//create Event
	MessageCallbackEvent event(id,buddy,state,error,errorString,EVT_MESSAGE_CALLBACK);
	//inform Chatwindow
	AddPendingEvent(event);
	//delete the sendMessageAction
	actions->deleteAction(id, error == MESSAGE_ERROR_NO);
}

void Impp::processAuthorisationRequest(Buddy* b,const wxString & authReason){
	//TODO: a lot of things have to be fixed for authorisation requests
	//TODO: Event no gui dependings

	notfications->registerAuthRequest(b,authReason);
	//new AuthConfirmDialog(buddyList,b,authReason);


	wxString caption = wxT("Authorize ");
	caption << b->getName() << wxT("?");


	wxString msg = b->getName();
	msg << wxT("want add you to his buddylist, authorize him? \n He sent \"");
	msg <<	authReason;
	msg << wxT("\" as Reason.");

	iBuddyList* bl = modules->getBuddyListModule();
	if(bl){
		wxMessageDialog msgDialog(bl->getWindow(),msg,caption,wxDEFAULT_DIALOG_STYLE|wxYES|wxNO);

		iProtocol* prot = getProtocolIfActive(b->getAccountId());
		if(prot == NULL){
			wxLogError(wxT("Impp::processAuthorisationRequest: A Buddy from an Inactive Account requests Authorisation???"));
			return;
		}

		if ( msgDialog.ShowModal() == wxID_YES ){
			prot->replayAuthorisation(b, true, wxEmptyString);
		}else{
			prot->replayAuthorisation(b, false, wxEmptyString);
		}
	}else{
		wxLogDebug(wxT("Impp::processAuthorisationRequest: Coudn't show dialog, no buddylist loaded"));
	}



}

void Impp::processCustomNameChange(Buddy* buddy,const wxString & newCustomName){
	wxASSERT(buddy!=NULL);

	wxString oldCustomName = buddy->getCustomName();
	//update the customname of the Buddy
	buddy->setCustomName(newCustomName);
	//update the name in the database
	db->updateBuddyCustomName(buddy,newCustomName);
	//update the buddylist

	if(!buddy->isSelf()){
		BuddyChangedEvent event(buddy, BUDDY_CHANGED_CUSTOMNAME, EVT_BUDDY_CHANGED);
		event.setOldCustomName(oldCustomName);
		event.setNewCustomName(newCustomName);
		AddPendingEvent(event);
	}else{
		SelfChangedEvent event(buddy, SELF_CHANGED_CUSTOMNAME, EVT_SELF_CHANGED);
		event.setOldCustomName(oldCustomName);
		event.setNewCustomName(newCustomName);
		AddPendingEvent(event);
	}
}

void Impp::processBuddyMove(Buddy* buddy, Group* newGroup){
	wxASSERT(buddy!=NULL);
	wxASSERT(newGroup!=NULL);

	Group* oldGroup = buddy->getGroup();
	oldGroup->removeBuddy(buddy);
	newGroup->addBuddy(buddy);
	db->updateGroup(buddy,newGroup);

	if(newGroup == notInListGroup){
		if(buddy->isInBuddyList()){
			buddy->setIsInBuddylist(false);
			db->updateBuddyFlags(buddy, buddy->getFlags());
		}
	}else{
		if(!buddy->isInBuddyList()){
			buddy->setIsInBuddylist(true);
			db->updateBuddyFlags(buddy, buddy->getFlags());
		}
	}

	BuddyChangedEvent event(buddy, BUDDY_CHANGED_GROUP, EVT_BUDDY_CHANGED);
	event.setNewGroup(newGroup);
	event.setOldGroup(oldGroup);

	AddPendingEvent(event);

}

void Impp::processOwnScreenNameChange(const db_id & accountId, const wxString & newScreenName){

	Account* account = getAccount(accountId);
	if(account == NULL){
		wxLogDebug(wxT("Impp::processOwnScreenNameChange: Invalid account id!"));
	}

	if(account->isActive()){
		wxString oldScreenName = account->self->getScreenName();
		account->self->setScreenName(newScreenName);

		db->updateBuddyScreenName(account->self,newScreenName);

		SelfChangedEvent event(account->self, SELF_CHANGED_SCREENNAME, EVT_SELF_CHANGED);
		event.setOldScreenName(oldScreenName);
		event.setNewScreenName(newScreenName);
		AddPendingEvent(event);
	}
}

void Impp::processFullUserInfoChange(Buddy* buddy){
	wxASSERT(buddy!=NULL);

	BuddyChangedEvent event(buddy, BUDDY_CHANGED_FULL_USERINFO, EVT_BUDDY_CHANGED);
	AddPendingEvent(event);
}

void Impp::changeBuddyCustomName(Buddy* buddy,const wxString & newCustomName){
	wxASSERT(buddy!=NULL);

	iProtocol* prot = getProtocolIfActive(buddy->getAccountId());
	if(prot != NULL){
		prot->changeCustomName(buddy,newCustomName);
		processCustomNameChange(buddy,newCustomName);
	}else{
		wxLogError(wxT("Impp::changeBuddyCustomName: Can not change Buddy Custom Name, the Account is not active!"));
	}
}

void Impp::changeBuddyGroup(Buddy* buddy, Group* newGroup){
	wxASSERT(buddy!=NULL);
	wxASSERT(newGroup!=NULL);

	iProtocol* prot = getProtocolIfActive(buddy->getAccountId());
	if(prot != NULL){
		prot->changeBuddyGroup(buddy,newGroup);
		processBuddyMove(buddy,newGroup);
	}else{
		wxLogError(wxT("Impp::changeBuddyGroup: Can not change Buddy Group, the Account is not active!"));
	}

}

void Impp::changeAddToPrivacyList(Buddy* buddy, const BuddyPrivacyList & privacyList){
	wxASSERT(buddy!=NULL);

	if(buddy->isOnPrivacyList(privacyList)){
		return;
	}

	iProtocol* prot = getProtocolIfActive(buddy->getAccountId());
	if(prot == NULL){
		wxLogError(wxT("Impp::changeAddToPrivacyList: Can not be done, Account not active"));
		return;
	}

	switch(privacyList){
		case BUDDY_PRIVACY_INVISIBLELIST:
			if(buddy->isOnPrivacyList(BUDDY_PRIVACY_VISIBLELIST)){
				prot->changeRemoveFromPrivacyList(buddy,BUDDY_PRIVACY_VISIBLELIST);
				processRemoveFromPrivacyList(buddy, BUDDY_PRIVACY_VISIBLELIST);
			}
			break;
		case BUDDY_PRIVACY_VISIBLELIST:
			if(buddy->isOnPrivacyList(BUDDY_PRIVACY_INVISIBLELIST)){
				prot->changeRemoveFromPrivacyList(buddy,BUDDY_PRIVACY_INVISIBLELIST);
				processRemoveFromPrivacyList(buddy, BUDDY_PRIVACY_INVISIBLELIST);
			}
			break;
		case BUDDY_PRIVACY_NOLIST:
		case BUDDY_PRIVACY_IGNORED:
			//nothing to do
			break;
	}

	prot->changeAddToPrivacyList(buddy, privacyList);
	processAddedToPrivacyList(buddy, privacyList);

}

void Impp::changeRemoveFromPrivacyList(Buddy* buddy, const BuddyPrivacyList & privacyList){
	wxASSERT(buddy!=NULL);

	iProtocol* prot = getProtocolIfActive(buddy->getAccountId());
	if(prot == NULL){
		wxLogError(wxT("Impp::changeRemoveFromPrivacyList: Can not be done, Account not active"));
		return;
	}

	prot->changeRemoveFromPrivacyList(buddy, privacyList);
	processRemoveFromPrivacyList(buddy, privacyList);
}

void Impp::changeBuddyPriority(Buddy* buddy,const BuddyPriority & flag){
	wxASSERT(buddy!=NULL);

	buddy->setPriority(flag);
	db->updateBuddyFlags(buddy, buddy->getFlags());

	BuddyChangedEvent event(buddy, BUDDY_CHANGED_PRIORITY, EVT_BUDDY_CHANGED);
	AddPendingEvent(event);
}

void Impp::changeOwnAvatar(const db_id & accountId, const wxString & file){

	if(!wxFile::Exists(file)){
		wxLogError(wxT("Impp::changeOwnAvatar: Could not change own Buddy Icon, file didn't exsist!"));
		return;
	}

	Account* account = getAccount(accountId);
	if(account == NULL){
		wxLogDebug(wxT("Impp::changeOwnAvatar: Invalid account id!"));
		return;
	}

	if(!account->isActive()){
		wxLogDebug(wxT("Impp::changeOwnAvatar: Can not change Avatar, Account not active!"));
		return;
	}

	wxImage img(file);
	int width =img.GetWidth();
	int height = img.GetHeight();


	wxString filename = wxString::Format(wxT("avatar%i.jpg"),account->id.ToLong());
	wxString dest = wxString::Format(wxT("%s/%s"),getAvatarDir().c_str(), filename.c_str());

	if(width > 128 || height > 128){
		if(height > width){
			img = img.Scale(128*width/height,128);
		}else{
			img = img.Scale(128,128*height/width);
		}
	}
	img.SaveFile(dest);

	iProtocol* prot = getProtocolIfActive(account->id);
	if(prot == NULL){
		wxLogDebug(wxT(" Impp::changeOwnAvatar: No Protocol found, should never happen!"));
		return;
	}
	if(prot->getConnectionState() == CONNECTION_STATE_CONNECTED){
		prot->changeOwnAvatar(dest);
	}else{
		wxLogError(wxT("Can not change Avatar, if not connected!"));
		return;
	}

	bool success = account->self->setAvatar(dest);
	if(success){
		db->setStringValue(account->self, wxT("avatar"), filename);

		AvatarChangedEvent event(account->self,EVT_OWN_AVATAR_CHANGED);
		AddPendingEvent(event);	;
	}

}

Group* Impp::getRootGroup(){
	return rootGroup;
}

Group* Impp::getNotInListGroup(){
	return notInListGroup;
}

Database* Impp::getDatabase(){
	return db;
}

std::vector<Account*> Impp::getAccounts(){
	return accounts;
}

void Impp::getActiveAccounts(std::vector<Account*> & out){
	std::vector<Account*>::iterator ia;
	for(ia = accounts.begin(); ia != accounts.end(); ia++){
		Account* account = *ia;
		if(account->isActive()){
			out.push_back(account);
		}
	}
}

Account* Impp::createAccount(const wxString & classname, const wxString & screenName, const wxString & password){
	Account* acc = db->createAccount(ACCOUNT_FLAG_ACTIVE, classname, screenName, password);

	iProtocol* prot = modules->loadAndInitProtocolInstance(acc);

	if(prot == NULL){
		wxLogDebug(wxT("Impp::init: Could not load Account"));
		delete acc;
		return NULL;
	}else{
		acc->protocol = prot;
		accounts.push_back(acc);
	}

	AccountEvent event(acc, ACCOUNT_EVENT_NEW_ACCOUNT, EVT_ACCOUNT);
	AddPendingEvent(event);

	return acc;
}

void Impp::deleteAccount(const db_id & id){
	Account* acc = NULL;

	std::vector<Account*>::iterator ia;
	for(ia = accounts.begin(); ia != accounts.end(); ia++){
		Account* account = *ia;
		if(account->id == id){
			acc = account;
			accounts.erase(ia);
			break;
		}
	}

	if(acc == NULL){
		wxLogDebug(wxT("Impp::deleteAccount: Could not find Accounf"));
		return;
	}

	AccountEvent event(acc, ACCOUNT_EVENT_ACCOUNT_DELETED, EVT_ACCOUNT);
	ProcessEvent(event);

	db->deleteAccount(id);
	delete acc;

	//TODO: A lot of work todo: delte buddies
}

Settings* Impp::getSettings(){
	return settings;
}


Group* Impp::getOrCreateGroup(const wxString & name){
	for (size_t i = 0; i < groups.size(); ++i) {
		Group* g = groups[i];
		if(g->getName()==name){
			return g;
		}
	}
	Group* group = db->createGroup(name);
	addGroupToGroupsAndBuddylist(group);
	return group;
}

Buddy* Impp::getOrCreateBuddy(const db_id & accountId, const wxString & screenName, Group* subGroup, const wxString & customName){
	for (size_t i = 0; i < buddies.size(); ++i) {
		Buddy* b = buddies[i];
		if(accountId == b->getAccountId() && screenName == b->getScreenName()){
			return b;
		}
	}

	int/*<BuddyFlags>*/ flags = BUDDY_FLAG_NOFLAG;

	if(subGroup == notInListGroup){
		flags = BUDDY_FLAG_NOT_INLIST|BUDDY_FLAG_NO_AUTHORIZATION;
	}

	Buddy* buddy = db->createBuddy(accountId, screenName,screenName,subGroup,flags);
	addBuddyToGroupsAndBuddylist(buddy,subGroup);
	return buddy;
}


std::vector<Buddy*>  Impp::getAllBuddies(){
	return buddies;
}

void Impp::getBuddiesFromAccount(const db_id & accountId, std::vector<Buddy*> & out){
	std::vector<Buddy*>::iterator ib;
	for(ib = buddies.begin(); ib != buddies.end(); ib++){
		Buddy* b = *ib;
		if(b->getAccountId() == accountId){
			out.push_back(b);
		}
	}
}

std::vector<Group*> Impp::getAllGroups(){
	return groups;
}

Buddy* Impp::getSelf(const db_id & accountId){
	std::vector<Account*>::iterator ia;
	for(ia =  accounts.begin(); ia != accounts.end(); ia++){
		Account* a = *ia;
		if(a->id == accountId){
			return a->self;
		}
	}
	return NULL;
}

ModuleManager* Impp::getModuleManager(){
	return modules;
}

void Impp::processMessageIn(Buddy* sender,const wxDateTime & time, wxString & msg, int/*<MessageEventType>*/ messageEventType){
	wxASSERT(sender!=NULL);
	db_id historyId = db->saveInHistory(time,sender,msg,false);
	sender->setHasUnredMsg(true);
	db->updateBuddyFlags(sender, sender->getFlags());

	MessageEvent event(sender,time,msg,historyId,messageEventType,EVT_MESSAGE_IN);

	AddPendingEvent(event);
	notfications->registerUnreadMsg(event);
}

void Impp::processBuddyAvatarReceived(const ActionId & id, Buddy* b,const wxString & file){
	wxLogDebug(wxT("new Avatar for %s with name %s"), b->getName().c_str(), file.c_str());

	wxString fullFile = wxString::Format(wxT("%s/%s"), getAvatarDir().c_str(), file.c_str());

	bool success = b->setAvatar(fullFile);
	if(success){
		db->setStringValue(b,wxT("avatar"), file);

		AvatarChangedEvent event(b,EVT_BUDDY_AVATAR_CHANGED);
		AddPendingEvent(event);	;
	}

	actions->deleteAction(id,true);
}

void Impp::processAvararRequestError(const ActionId & id){
	actions->deleteAction(id,false);
}

void Impp::processTypingNotification(Buddy* b, TypingState state){
	TypingNotificationEvent event(b ,state, EVT_TYPING_NOTIFICATION);
	AddPendingEvent(event);
}

void Impp::processBuddyNoAuthorisationAnymore(Buddy* buddy){
	buddy->setHasAuthorisation(false);
	db->updateBuddyFlags(buddy, buddy->getFlags());

	BuddyChangedEvent event(buddy, BUDDY_CHANGED_AUTHORISATION, EVT_BUDDY_CHANGED);
	AddPendingEvent(event);
}

void Impp::processAuthorisationGranded(Buddy* buddy){
	buddy->setHasAuthorisation(true);
	db->updateBuddyFlags(buddy, buddy->getFlags());

	BuddyChangedEvent event(buddy, BUDDY_CHANGED_AUTHORISATION, EVT_BUDDY_CHANGED);
	AddPendingEvent(event);
}

void Impp::processSettingChangedInt(const DBModule & module, const wxString & settingName, int newValue){
	SettingChangedEvent event(module, settingName, EVT_SETTING_CHANGED);
	event.SetInt(newValue);
	AddPendingEvent(event);
}
void Impp::processSettingChangedString(const DBModule & module, const wxString & settingName, const wxString & newValue){
	SettingChangedEvent event(module, settingName, EVT_SETTING_CHANGED);
	event.SetString(newValue);
	AddPendingEvent(event);
}

void Impp::processCommandAction(const wxString & moduleName, const wxString & action, bool isChecked, Buddy* buddy, Group* group){
	ImppCommandEvent event(moduleName, action, EVT_IMPP_COMMAND);
	event.setBuddy(buddy);
	event.setGroup(group);
	event.SetInt(isChecked ? 1 : 0);
	AddPendingEvent(event);
}

ActionId Impp::sendMessage(Buddy* buddy, const wxString & msg){
	//popups->showPopup(msg,3);
	ActionId id = actions->insertAction(new SendMessageAction(buddy));


	//wxLogDebug(wxT("id: %i"), buddy->getAccountId().ToLong());

	iProtocol* prot = getProtocolIfActive(buddy->getAccountId());

	if(prot == NULL){
		processMessageSendCallback(id,buddy,MESSAGE_STATE_FAILED,MESSAGE_ERROR_SELF_OFFLINE);
		return id;
	}

	if(prot->getConnectionState()!=CONNECTION_STATE_CONNECTED){
		processMessageSendCallback(id,buddy,MESSAGE_STATE_FAILED,MESSAGE_ERROR_SELF_OFFLINE);
		return id;
	}

	wxDateTime time = wxDateTime::Now();
	db_id historyId = db->saveInHistory(time,buddy,msg,true);

	prot->sendMessage(id, buddy, msg);

	MessageEvent event(buddy,time,msg,historyId,MESSAGE_EVENT_NORMAL_MESSAGE,EVT_MESSAGE_OUT);
	AddPendingEvent(event);

	return id;
}

ActionId Impp::processIncomingFileTransfare(Buddy* buddy, const wxString & filename, size_t fileCount, size_t fileSize){
	ActionId id = actions->insertAction(new IncomingFileTransfareAction(buddy));

	FileTransfareEvent event(id, buddy, filename, fileCount, fileSize, EVT_FILE_TRANSFARE);
	AddPendingEvent(event);


	//if(fileTransWin == NULL){
	//	fileTransWin = new FileTransfareWindow(buddyList);
	//}


	//fileTransWin->addFileTransfareInfo(event);




	return id;
}

void Impp::processIncomingFileAccept(const ActionId & id, bool accept){
	Action* action = actions->getAction(id);
	if(action == NULL){
		wxLogDebug(wxT("Impp::processIncomingFileAccep: Action not found"));
		return;
	}
	Buddy* buddy = action->getBuddy();

	if(buddy != NULL){

		iProtocol* prot = getProtocolIfActive(buddy->getAccountId());
		if(prot != NULL){
			prot->replayFileTransfare(id, buddy, accept);
		}else{
			wxLogError(wxT("Impp::processIncomingFileAccept: Can not be done, Account not active???"));
			return;
		}

	}else{
		wxLogError(wxT("Impp::processIncomingFileAccept: ActionBuddy was null!"));
	}

}


void Impp::sendTypingNotification(Buddy* b, TypingState state){
	if(b->getStatus()==OFFLINE){
		wxLogDebug(wxT("Impp::sendTypingNotification: Coudn't send typing notification, Buddy is offline"));
		return;
	}

	iProtocol* prot = getProtocolIfActive(b->getAccountId());
	if(prot != NULL){
		if(prot->getConnectionState() != CONNECTION_STATE_CONNECTED){
			wxLogDebug(wxT("Impp::sendTypingNotification: Coudn't send typing notification, you are offline"));
		}else{
			prot->sendTypingNotification(b, state);
		}
	}else{
		wxLogDebug(wxT("Impp::sendTypingNotification: Can not send typing Notification, Accoun not active!"));
	}



}

void Impp::close(){
	delete modules;

	if(notfications){
		delete notfications;
	}
	if(settings){
		delete settings;
	}
	if(actions){
		delete actions;
	}

	for (size_t i = 0; i < buddies.size(); ++i) {
		delete buddies[i];
	}
	for (size_t i = 0; i < groups.size(); ++i) {
		delete groups[i];
	}
	if(db){
		delete db;
	}
	for (size_t i = 0; i < accounts.size(); ++i) {
		delete accounts[i];
	}

	exit(EXIT_SUCCESS);
}

ActionId Impp::connect(const db_id & accountId){

	wxLogTrace(wxT("IMPP"), wxT("Impp::connect: called with accountId=%i"),
			accountId.ToLong());

	ActionId id = -1;

	Account* account = getAccount(accountId);

	if(account == NULL){
		wxLogDebug(wxT("Impp::connect: Invalid account id!"));
		return id;
	}

	if(!account->isActive()){
		wxLogDebug(wxT("Impp::connect: Account is not active!"));
		return id;
	}

	iProtocol* prot = account->protocol;

	if(prot == NULL){
		wxLogError(wxT("Impp::connect: Can not be done, Account not active"));
		return id;
	}

	ConnectionState connectionState = prot->getConnectionState();

	if(connectionState==CONNECTION_STATE_DISCONNECTED){

		Buddy* self = account->self;
		wxASSERT(self != NULL);

		id = actions->insertAction(new ConnectAction(self));

		Status status = self->getStatus();
		status.clearFlag(OFFLINE);

		prot->connect(id, status, self->getStatusMsg(), prot->getPrivacySettings());

	}else{
		wxLogDebug(wxT("Impp::connect: Coudn't connect, beacause you are already connected or your are allready connecting!"));
	}

	return id;
}

PrivacySetting Impp::getPrivacySettings(const db_id & accountId){

	iProtocol* prot = getProtocolIfActive(accountId);
	if(prot != NULL){
		return prot->getPrivacySettings();
	}

	wxLogDebug(wxT("Impp::getPrivacySettings: can not get settings, account is not active"));
	return PRICACY_INVALID;
}

void Impp::setPrivacySettings(const db_id & accountId, const PrivacySetting & visibility){

	Account* account = getAccount(accountId);

	if(account == NULL){
		wxLogDebug(wxT("Impp::setPrivacySettings: Invalid account id!"));
		return;
	}

	if(!account->isActive()){
		wxLogDebug(wxT("Impp::setPrivacySettings: Account is not active!"));
		return;
	}


	iProtocol* prot = account->protocol;

	if(prot == NULL){
		wxLogDebug(wxT("Impp::setPrivacySettings: can not set settings, account is not active"));
		return;
	}

	if(prot->getPrivacySettings()==visibility){
		wxLogDebug(wxT("Impp::setPrivacySettings: ignoring, already set"));
		return;
	}

	Buddy* self = account->self;
	wxASSERT(self != NULL);

	Status newStatus = self->getStatus();

	if(visibility == PRICACY_ALLOW_ONLY_PERMITLIST){
		newStatus.setFlag(INVISIBLE);
	}else{
		newStatus.clearFlag(INVISIBLE);
	}

	prot->setPrivacySettings(visibility);
	if(prot->getConnectionState() == CONNECTION_STATE_CONNECTED){
		setStatus(accountId, newStatus, self->getStatusMsg());
	}else{
		processOwnStatusChanged(account->id, -1, newStatus);
	}


}

void Impp::disconnect(const db_id & accountId){

	Account* account = getAccount(accountId);

	if(account == NULL){
		wxLogDebug(wxT("Impp::disconnect: Invalid account id!"));
		return;
	}

	if(!account->isActive()){
		wxLogDebug(wxT("Impp::disconnect: Account is not active!"));
		return;
	}

	iProtocol* prot = account->protocol;

	if(prot != NULL){
		wxLogTrace(wxT("IMPP"), wxT("Impp::disconnect: calling disconnect for Protocol of Account (%i)"), account->id.ToLong());
		prot->disconnect();
	}else{
		wxLogDebug(wxT("Impp::disconnect: Can not be done, Account not active"));
	}

}

ConnectionState Impp::getConnectionState(const db_id & accountId){

	Account* account = getAccount(accountId);

	iProtocol* prot = account->protocol;
	if(prot != NULL){
		return prot->getConnectionState();
	}else{
		return CONNECTION_STATE_INVALID;
	}


}

ActionId Impp::setStatus(const db_id & accountId, const Status & status, const wxString & statusMsg){

	wxLogTrace(wxT("IMPP"), wxT("Impp::setStatus: called with accountId=%i, status=%s, statusMsg=%s"),
			accountId.ToLong(), statusToString(status).c_str(), statusMsg.c_str());

	ActionId actionId = -1;

	Account* account = getAccount(accountId);

	if(account == NULL){
		wxLogDebug(wxT("Impp::setStatus: Invalid account id!"));
		return actionId;
	}

	if(!account->isActive()){
		wxLogDebug(wxT("Impp::setStatus: Account is not active!"));
		return actionId;
	}

	iProtocol* prot = account->protocol;

	if(prot != NULL){
		actionId = actions->insertAction(new SetStatusAction(account->self));

		Status newStatus = status;

		if(prot->getPrivacySettings() == PRICACY_ALLOW_ONLY_PERMITLIST){
			newStatus.setFlag(INVISIBLE);
		}else{
			newStatus.clearFlag(INVISIBLE);
		}

		//TODO: corect flags

		account->self->setRawStatusMsg(statusMsg);
		StatusEvent evt(account->self, account->self->status, newStatus, account->self->getStatusMsg(), statusMsg, STATUS_EVENT_BUDDY_STATUS_CHANGED, STATUS_EVENT_SELF_CONNECTED, EVT_OWN_STATUS_CHANGING);
		ProcessEvent(evt);

		wxString newStatusMsg = evt.getStatusMsg();

		account->self->setStatusMsg(newStatusMsg);

		if(prot->getConnectionState() == CONNECTION_STATE_DISCONNECTED){
			newStatus.clearFlag(ONLINE);
			newStatus.setFlag(OFFLINE);

			processOwnStatusChanged(accountId, actionId, newStatus);
		}else{
			if(prot->getConnectionState() == CONNECTION_STATE_CONNECTING){
				newStatus.clearFlag(ONLINE);
				newStatus.setFlag(OFFLINE);
			}else{
				newStatus.clearFlag(OFFLINE);
			}
			prot->setStatus(actionId, newStatus, newStatusMsg);
		}


	}else{
		wxLogDebug(wxT("Impp::setStatus: Can not be done, Protocol not found"));
	}



	return actionId;
}

ActionId Impp::setStatusAndChageState(const db_id & accountId, const Status & status,const wxString & statusMsg, const PrivacySetting & privacy){

	wxLogTrace(wxT("IMPP"), wxT("Impp::setStatusAndChageState: called with accountId=%i, status=%s, statusMsg=%s"),
			accountId.ToLong(), statusToString(status).c_str(), statusMsg.c_str());

	ActionId actionId = -1;

	Account* account = getAccount(accountId);

	if(account == NULL){
		wxLogDebug(wxT("Impp::setStatusAndChageState: Invalid account id!"));
		return actionId;
	}

	if(!account->isActive()){
		wxLogDebug(wxT("Impp::setStatusAndChageState: Account is not active!"));
		return actionId;
	}

	if(privacy != PRICACY_INVALID){
		setPrivacySettings(accountId, privacy);
	}

	if(status != OFFLINE){
		actionId = setStatus(accountId, status, statusMsg);
	}


	iProtocol* prot = account->protocol;

	if(prot == NULL){
		wxLogDebug(wxT("Impp::setStatusAndChageState: Protocol was NULL!"));
		return actionId;
	}

	ConnectionState state = prot->getConnectionState();

	if(status != OFFLINE && state == CONNECTION_STATE_DISCONNECTED){
		actionId = connect(accountId);
	}else if(status == OFFLINE && state != CONNECTION_STATE_DISCONNECTED){
		disconnect(accountId);
	}

	return actionId;

}

void Impp::processOwnStatusChanged(const db_id & accountId, const ActionId & id, const Status & status){

		Account* account = getAccount(accountId);

		if(account == NULL){
			wxLogError(wxT("Impp::processOwnStatusChanged: illegal Account id"));
			return;
		}

		if(account->isActive()){

			Buddy* self = account->self;

			Status oldStatus = self->getStatus();
			self->setStatus(status);

			StatusConnectionType conType = STATUS_EVENT_SELF_CONNECTED;
			StatusEventType type = STATUS_EVENT_BUDDY_STATUS_CHANGED;

			if(oldStatus == OFFLINE && status != OFFLINE){
				conType = STATUS_EVENT_SELF_CONNECTION;
				type = STATUS_EVENT_BUDDY_SIGNED_ON;
			}
			if(oldStatus != OFFLINE && status == OFFLINE){
				conType = STATUS_EVENT_SELF_DISCONNECTION;
				type = STATUS_EVENT_BUDDY_SIGNED_OFF;
			}

			StatusEvent event(self, oldStatus, status, self->getStatusMsg(), self->getStatusMsg(), type, conType, EVT_OWN_STATUS_CHANGED);
			AddPendingEvent(event);

			if(conType == STATUS_EVENT_SELF_DISCONNECTION){
				std::vector<Buddy*>::iterator ib;
				for(ib = buddies.begin(); ib != buddies.end(); ib++){
					Buddy* buddy = *ib;

					if(buddy->accountId == account->id){
						processBuddyStatusChanged(buddy, UNKNOWN, wxEmptyString, STATUS_EVENT_SELF_DISCONNECTION);
					}
				}
			}

			actions->deleteAction(id, true);
		}else{
			wxLogDebug(wxT("Impp::processOwnStatusChanged: Account is not active"));
		}



}

void Impp::processConnectionEstablished(const db_id & accountId, const ActionId & id, const Status & status){

	wxLogTrace(wxT("IMPP"), wxT("Impp::processConnectionEstablished: called with accountId=%i"),
			accountId.ToLong());

	processOwnStatusChanged(accountId, id, status);
	//actions->deleteAction(id, true); allready doing statusChanged
}

void Impp::processConnectionLost(const db_id & accountId, const ActionId & id, const wxString & reason){

	Status newStatus = OFFLINE;
	if(getPrivacySettings(accountId) == PRICACY_ALLOW_ONLY_PERMITLIST){
		newStatus.setFlag(INVISIBLE);
	}

	actions->deleteAction(id, false);

	processOwnStatusChanged(accountId, id, newStatus);
}

void Impp::processUnreadMessageRead(Buddy* buddy){
	buddy->setHasUnredMsg(false);
	db->updateBuddyFlags(buddy, buddy->getFlags());

	notfications->unregisterAllUnreadMsgs(buddy);

	MessageReadEvent event(buddy, EVT_MESSAGE_READ);
	AddPendingEvent(event);
}

void Impp::processAddedToPrivacyList(Buddy* buddy, const BuddyPrivacyList & privacyList){
	buddy->addToPrivacyList(privacyList);
	db->updateBuddyFlags(buddy, buddy->getFlags());


	BuddyChangedEvent event(buddy, BUDDY_CHANGED_PRIVACY_LIST, EVT_BUDDY_CHANGED);
	AddPendingEvent(event);
}

void Impp::processRemoveFromPrivacyList(Buddy* buddy, const BuddyPrivacyList & privacyList){
	buddy->removeFromPrivacyList(privacyList);
	db->updateBuddyFlags(buddy, buddy->getFlags());

	BuddyChangedEvent event(buddy, BUDDY_CHANGED_PRIVACY_LIST, EVT_BUDDY_CHANGED);
	AddPendingEvent(event);
}

Buddy* Impp::getNextBuddyWithUnreadMsgs(){
	return notfications->getNextBuddyWithUnreadMsgs();
}

ActionId Impp::requestAvatar(Buddy* buddy){
	iProtocol* prot = getProtocolIfActive(buddy->getAccountId());
	if(prot == NULL){
		wxLogDebug(wxT("Impp::requestAvatar: Can not be done, Account not active"));
		return -1;
	}


	if(prot->getConnectionState()==CONNECTION_STATE_CONNECTED){
		Action* action = actions->getAction(wxT("RequestAvatarAction"), buddy);
		ActionId id = -1;

		if(action == NULL){
			id = actions->insertAction(new RequestAvatarAction(buddy));
			prot->requestAvatar(id, buddy);
		}else{
			id = action->getId();
			wxLogTrace(wxT("AVATAR"),wxT("Can not request Avatar, allready requesting..."));
		}

		return id;
	}else{
		wxLogTrace(wxT("AVATAR"),wxT("Can not request Avatar if not connected..."));
	}
	return -1;
}

void Impp::requestShortUserInfo(Buddy* buddy){
	iProtocol* prot = getProtocolIfActive(buddy->getAccountId());
	if(prot == NULL){
		wxLogDebug(wxT("Impp::requestShortUserInfo: Can not be done, Account not active"));
		return;
	}

	if(prot->getConnectionState()==CONNECTION_STATE_CONNECTED){
		prot->requestShortUserInfo(buddy);
	}else{
		wxLogTrace(wxT("AVATAR"),wxT("Can not request ShortUserInfo if not connected..."));
	}
}

void Impp::requestFullUserInfo(Buddy* buddy){
	iProtocol* prot = getProtocolIfActive(buddy->getAccountId());
	if(prot == NULL){
		wxLogDebug(wxT("Impp::requestFullUserInfo: Can not be done, Account not active"));
		return;
	}

	if(prot->getConnectionState()==CONNECTION_STATE_CONNECTED){
		prot->requestFullUserInfo(buddy);
	}else{
		wxLogTrace(wxT("AVATAR"),wxT("Can not request ShortUserInfo if not connected..."));
	}
}


void Impp::checkBuddyStatus(Buddy* buddy){
	iProtocol* prot = getProtocolIfActive(buddy->getAccountId());
	if(prot == NULL){
		wxLogDebug(wxT("Impp::checkBuddyStatus: Can not be done, Account not active"));
		return;
	}

	if(prot->getConnectionState()==CONNECTION_STATE_CONNECTED){
		prot->checkUserStatus(buddy);
	}else{
		wxLogDebug(wxT("Can not Check Status not connected...!"));
	}
}

void Impp::requestStatusMessage(Buddy* buddy){
	iProtocol* prot = getProtocolIfActive(buddy->getAccountId());
	if(prot == NULL){
		wxLogDebug(wxT("Impp::requestStatusMessage: Can not be done, Account not active"));
		return;
	}

	if(prot->getConnectionState()==CONNECTION_STATE_CONNECTED){
		prot->requestStatusMessage(buddy);
	}else{
		wxLogTrace(wxT("AWAY"),wxT("Can not request Status Message if not connected..."));
	}
}

ActionManager* Impp::getActionManager(){
	return actions;
}

iProtocol* Impp::getProtocolIfActive(const db_id & accountId){
	std::vector<Account*>::iterator ia;
	for(ia = accounts.begin(); ia != accounts.end(); ia++){
		Account* a = *ia;
		if(a->isActive() && a->id == accountId){
			return a->protocol;
		}
	}
	return NULL;
}

Account* Impp::getAccount(const db_id & accountId){
	std::vector<Account*>::iterator ia;
	for(ia = accounts.begin(); ia != accounts.end(); ia++){
		Account* account = *ia;
		if(account->id == accountId){
			return account;
		}
	}
	return NULL;
}

size_t Impp::getActiveAccountCount(){
	size_t count = 0;
	std::vector<Account*>::iterator ia;
	for(ia = accounts.begin(); ia != accounts.end(); ia++){
		Account* account = *ia;
		if(account->isActive()){
			count++;
		}
	}
	return count;
}

PrivacySetting Impp::getMasterPrivacy(){
	std::vector<Account*>::iterator ia;
	for(ia = accounts.begin(); ia != accounts.end(); ia++){
		Account* account = *ia;
		if(account->isActive()){
			iProtocol* prot = account->protocol;
			if(prot != NULL){
				if(prot->getPrivacySettings() == PRICACY_ALLOW_ONLY_PERMITLIST){
					return PRICACY_ALLOW_ONLY_PERMITLIST;
				}
			}
		}
	}
	return PRICACY_BLOCK_ONLY_DENYLIST;
}

Status Impp::getMasterStatus(){
	if(accounts.size() == 1){
		return accounts[0]->self->getStatus();
	}else{
		wxLogDebug(wxT("Impp::getMasterStatus: TODO:::"));
		return accounts[0]->self->getStatus();
	}
}

void Impp::changeAccountInfo(const db_id & accountId, const wxString & newScreenName, const wxString & newPassword){
	Account* acc = getAccount(accountId);
	if(acc == NULL){
		wxLogError(wxT("Impp::changeAccountInfo: Illegal Account id!"));
		return;
	}

	acc->screenname = newScreenName;
	acc->password = newPassword;

	db->updateAccount(acc);

	processOwnScreenNameChange(accountId, newScreenName);

	AccountEvent event(acc, ACCOUNT_EVENT_ACCOUNT_CHANGED, EVT_ACCOUNT);
	AddPendingEvent(event);

}

}
